package it.trekker.statistics;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import android.location.Location;
import android.util.Log;
import it.trekker.db.ExcursionCoordinate;

public class GPSStatistics implements Serializable {

	private static final String TAG = GPSStatistics.class.getSimpleName();
	
	public static final double UNDEFINED_PACE = -9999;
	public static final double UNDEFINED_ALTITUDE = -9999;

	private List<ExcursionCoordinate> lastLocations;
	private double distance;
	private double lastPartialDistance;
	private Date startTime;
	private Date endTime;
	private double pace;
	private double startAltitude;
	private double currentAltitude;
	private double upAltitudeDifference;
	private double downAltitudeDifference;
	private double totAltitudeDifference;
	private double meanPace;
	private long time;
	
	

	public GPSStatistics() {
		reset();
	}

	public List<ExcursionCoordinate> getLastLocations() {
		return lastLocations;
	}

	public void setLastLocations(List<ExcursionCoordinate> lastLocations) {
		this.lastLocations = lastLocations;
		
		if(lastLocations.size() > 0) {
			startTime = lastLocations.get(0).getTimestamp();
			endTime = lastLocations.get(lastLocations.size()-1).getTimestamp();
			startAltitude = lastLocations.get(0).getCoordinate().getAltitude();
		}
	}

	public double getDistance() {
		return distance;
	}

	public double getLastPartialDistance() {
		return lastPartialDistance;
	}

	public double getPace() {
		return pace;
	}

	public double getMeanPace() {
		return meanPace;
	}

	public long getTime() {
		return time;
	}

	public Date getStartTime() {
		return startTime;
	}

	public Date getEndTime() {
		return endTime;
	}

	public double getStartAltitude() {
		return startAltitude;
	}

	public double getCurrentAltitude() {
		return currentAltitude;
	}

	public double getUpAltitudeDifference() {
		return upAltitudeDifference;
	}

	public double getDownAltitudeDifference() {
		return downAltitudeDifference;
	}

	public double getTotAltitudeDifference() {
		return totAltitudeDifference;
	}

	private void updatePace() {

		int size = lastLocations.size();

		if(size >= 2) {
			float relativeDistance = distanceBetween(size-2, size-1);
			long startTime = lastLocations.get(size-2).getTimestamp().getTime();	// milliseconds
			long endTime = lastLocations.get(size-1).getTimestamp().getTime();	// milliseconds
			float diffTime = (endTime - startTime)/1000f;	// seconds

			if(diffTime > 0) {
				pace = relativeDistance / diffTime;	// m/s
			}
			else {
				pace = UNDEFINED_PACE;
			}
		}
		
		// TODO possibile usare
		//		pace = lastLocations.get(lastLocations.size()-1).getLocation().getSpeed();
	}
	
	private void updateMeanPace() {
		long start = startTime.getTime();	// milliseconds
		long end = lastLocations.get(lastLocations.size()-1).getTimestamp().getTime();	// milliseconds
		float diffTime = (end - start)/1000f;	// seconds

		if(diffTime > 0) {
			meanPace = distance / diffTime;	// m/s
		}
		else {
			meanPace = UNDEFINED_PACE;
		}
	}

	private void updateDistance() {

		//		while(lastLocations.size() > 1) {
		//			distance += distanceBetween(0, 1);
		//			lastLocations.remove(0);
		//		}

		int size = lastLocations.size();

		if(size >= 2) {
			for(int i = 0 ; i < size-1 ; i++) {
				Log.d(TAG, "dis = " + distanceBetween(i, i+1));
				lastPartialDistance = distanceBetween(i, i+1);
				distance += lastPartialDistance;
			}
		}
	}

	private void updateAltitude() {
		currentAltitude = lastLocations.get(lastLocations.size()-1).getCoordinate().getAltitude();
	}

	private void updateAltitudeDifference() {

		int size = lastLocations.size();

		if(size >= 2) {
			for(int i = 0 ; i < size-1 ; i++) {
				double firstAltitude = lastLocations.get(i).getCoordinate().getAltitude();
				double secondAltitude = lastLocations.get(i+1).getCoordinate().getAltitude();
				double difference = secondAltitude - firstAltitude;

				if(difference < 0) {
					downAltitudeDifference += difference;
				}
				else {
					upAltitudeDifference += difference;
				}

				totAltitudeDifference += difference;
			}
		}
	}
	
	private void updateTime() {
		if(startTime != null && endTime != null) {
			time = endTime.getTime() - startTime.getTime();
		}
	}

	/**
	 * Compute the distance between two location in lastLocations list
	 * 
	 * @param startIndex: index of the start location in lastLocations list
	 * @param endIndex: index of the end location in lastLocations list
	 * @return distance in meters
	 */
	private float distanceBetween(int startIndex, int endIndex) {

		float[] localDistance = new float[3];
		ExcursionCoordinate startLoc = lastLocations.get(startIndex);
		ExcursionCoordinate endLoc = lastLocations.get(endIndex);
		double startLatitude = startLoc.getCoordinate().getLatitude();
		double startLongitude = startLoc.getCoordinate().getLongitude();
		double endLatitude = endLoc.getCoordinate().getLatitude();
		double endLongitude = endLoc.getCoordinate().getLongitude();

		Location.distanceBetween(startLatitude, startLongitude, endLatitude, endLongitude, localDistance); 

		return localDistance[0];
	}

	
	private void logPrintLocations() {
		for(ExcursionCoordinate coord : lastLocations) {
			Log.d(TAG, coord.toString());
		}
	}
	
	
	//**************************************************//
	//***************** Public methods *****************//
	//**************************************************//

	public void addLocation(ExcursionCoordinate coord) {
		lastLocations.add(coord);
		
		if(startTime == null) {
			startTime = coord.getTimestamp();
			startAltitude = coord.getCoordinate().getAltitude();
		}
		
		endTime = coord.getTimestamp();
		
//		for(int i = 0 ; i < lastLocations.size() ; i++) 
//			Log.d(TAG, i + "-" + lastLocations.get(i));
	}
	
	public void update() {
		if(lastLocations.size() > 0) {
			updateDistance();
			updateAltitude();
			updateAltitudeDifference();
			updatePace();
			updateMeanPace();
			updateTime();

			while(lastLocations.size() > 1) {
				lastLocations.remove(0);
			}
		}
		else {
			reset();
		}
	}
	
	public void reset() {
		lastLocations = new ArrayList<ExcursionCoordinate>();
		
		startTime = null;
		endTime = null;
		
		distance = 0;
		lastPartialDistance = 0;
		upAltitudeDifference = 0;
		downAltitudeDifference = 0;
		totAltitudeDifference = 0;
		startAltitude = UNDEFINED_ALTITUDE;
		currentAltitude = UNDEFINED_ALTITUDE;
		pace = UNDEFINED_PACE;
		meanPace = UNDEFINED_PACE;
		time = 0;
	}

}
